package fr.asip.cps3.exemple.modele.traitements.fonctionsPKCS11.verifications;

import iaik.pkcs.pkcs11.wrapper.CK_MECHANISM;
import iaik.pkcs.pkcs11.wrapper.PKCS11;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;

import java.io.FileInputStream;
import java.io.IOException;

import org.apache.log4j.Logger;

import fr.asip.cps3.exemple.modele.exceptions.ExceptionProgrammeExemple;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionTraitement;
import fr.asip.cps3.exemple.modele.resultats.Resultat;
import fr.asip.cps3.exemple.modele.resultats.ResultatTriple;
import fr.asip.cps3.exemple.modele.traitements.util.TraitementsUtil;

/**
 * Classe de traitement : Vérification de la signature d'un fichier
 *
 */
public class VerifierSignatureFichier{

	/**
	 * Le loggeur
	 */
	 private static Logger log = Logger.getLogger(VerifierSignatureFichier.class);
	
	/**
	 * Renvoie le résultat de l'opération
	 * @param librairie Librairie PKCS#11 à utiliser
	 * @param idSession Identifiant de la session
	 * @param typeAlgo Type de l'algorithme
	 * @param idCle Identifiant de la clé publique
	 * @param fis Flux du fichier à signer
	 * @param signature Signature à vérifier
	 * @return Résultat de l'opération 
	 * @throws ExceptionTraitement
	 */
	public static Resultat traitementP11(PKCS11 librairie, long idSession, long typeAlgo, long idCle, FileInputStream fis, String signature) throws ExceptionTraitement {

		try {
			
			if(librairie != null) {
	
				if(fis != null) {
		
					ResultatTriple resultat = new ResultatTriple("Vérification de la signature d'un fichier avec "+TraitementsUtil.getNomAlgo(typeAlgo)+" (Initialisation (C_VerifyInit))", "Vérification de la signature d'un fichier avec "+TraitementsUtil.getNomAlgo(typeAlgo)+" (Vérification (C_VerifyUpdate))", "Vérification de la signature d'un fichier (Finalisation (C_VerifyFinal))");
					
					CK_MECHANISM algo = new CK_MECHANISM();
					algo.mechanism=typeAlgo;
				
					byte[] signatureOctets = TraitementsUtil.getOctetsSansFormatage(signature);
					boolean erreurSurvenue = false;
					
					// Traitements à tester
					try {
	
						//# INIT #//
						librairie.C_VerifyInit(idSession, algo, idCle);
						
					} catch (PKCS11Exception e) {
						
						log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
						resultat.setCodeRetour(e.getErrorCode());
						erreurSurvenue = true;
						resultat.setCodeRetour2(TraitementsUtil.AUCUN_CODE_RETOUR);
						resultat.appendOperation2(" NON EXECUTEE!");
						resultat.setCodeRetour3(TraitementsUtil.AUCUN_CODE_RETOUR);
						resultat.appendOperation3(" NON EXECUTEE!");
						
					}
					
					try {
					
						//# VERIFICATION DE LA SIGNATURE PAR UPDATES #//
						if(!erreurSurvenue) {
						    int retourLecture = 0;
						    boolean eof = false;
						    int longeurBlocsLecture = 1024;
						    
						     while (!eof)
						     {
						    	 // Lecture par blocs
						    	 retourLecture = 0;
						         byte[] donneesAVerifier = new byte[longeurBlocsLecture];
						         retourLecture = fis.read(donneesAVerifier, 0, longeurBlocsLecture);
		
						         // Plus rien à lire
						         if (retourLecture == -1)
						             break;
						         librairie.C_VerifyUpdate(idSession, donneesAVerifier);
						     }
						}
						
					} catch (IOException e) {
						
						log.error("Probleme d'entree/sortie\n"+e.getMessage());
						e.printStackTrace();
						erreurSurvenue = true;
						resultat.setCodeRetour3(TraitementsUtil.AUCUN_CODE_RETOUR);
						resultat.appendOperation3(" NON EXECUTEE!");
						
						
					} catch (PKCS11Exception e) {
						
						log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
						resultat.setCodeRetour2(e.getErrorCode());
						erreurSurvenue = true;
						resultat.setCodeRetour3(TraitementsUtil.AUCUN_CODE_RETOUR);
						resultat.appendOperation3(" NON EXECUTEE!");
						
					}
					
					try {
						
						//# FINAL #//
						if(!erreurSurvenue) {
							librairie.C_VerifyFinal(idSession,signatureOctets);
						}
						
					} catch (PKCS11Exception e) {
						
						log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
						resultat.setCodeRetour3(e.getErrorCode());
						
					}
					
					return resultat;
							
				} else {
					
					// Si le fichier est illisible il est impossible de réaliser l'opération
					log.error("Le fichier est illisible");
					throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_VERIFICATION_FICHIER, "Le fichier est illisible");				
				
				}
		
			} else {
				
				// Si la librairie est nulle il est impossible de réaliser l'opération
				log.error("La librairie est nulle");
				throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_VERIFICATION_FICHIER, "La librairie est nulle");
				
			}
		
		} catch (Throwable t) {
		
			// Si une erreur Traitement a été rencontrée on lève l'exception
			if(t instanceof ExceptionTraitement)
				throw (ExceptionTraitement)t;
			
			// Si une erreur inattendue est survenue on la logue et on la lève
			log.error("Une erreur inattendue est survenue : "+ExceptionProgrammeExemple.getStacktrace(t));
			throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_VERIFICATION_FICHIER, "Une erreur inattendue est survenue");
		
		}
	}
}
